#include <cassert>
#include <iostream>
using namespace std;

class Markdown{
  enum State{
    S_INIT,       // 初始状态
    S_HEADING,    // #标题
    S_HYPHEN,     // -无序列表、---横线
  };
  enum Block{     // 语句块的类型
    B_INIT,       // 不在块内
    B_ULIST,      // 列表
    B_PARAGRAPH,  // 段落
  };

  State state;    // 解析状态
  Block block;    // 块类型
  istream& ist;   // 输入流
  ostream& ost;   // 输出流

  void begin_html();
  void end_html();
  void blankline();
  void heading(const string& s, int level);
  void horizontal_rule();
  void ulist_begin();
  void ulist_item(const string& s);
  void ulist_end();
  void paragraph_end();
  void scan_text(const string& s, int i);
  void scan_line(const string& s);
  void end_block(bool accept_init);
public:
  Markdown(istream& src, ostream& dst);
  void render();
};

Markdown::Markdown(istream& src, ostream& dst)
  : state(S_INIT), block(B_INIT), ist(src), ost(dst) {
}

void Markdown::blankline(){
  string s;
  getline(ist, s);
  assert(!ist || s.empty()); // 要么EOF，要么读到空行。
}

void Markdown::heading(const string& s, int level){
  // TODO: 输出level级的标题
  blankline(); // 之后必须有空行
}

void Markdown::horizontal_rule(){
  // TODO: 输出分割线
  blankline();
}

void Markdown::ulist_begin(){
  ost << "<ul>\n";
}

void Markdown::ulist_item(const string& s){
  ost << "  <li>";
  scan_text(s, 0); // 修饰列表文本
  ost << "</li>\n";
}

void Markdown::ulist_end(){ // 列表结束
  ost << "</ul>\n\n";
  block = B_INIT;
  state = S_INIT;
}

void Markdown::paragraph_end(){ // 段落结束
  ost << "</p>\n\n";
  block = B_INIT;
  state = S_INIT;
}

void Markdown::scan_text(const string& s, int i){ // 文本修饰
  enum TextState{ // 文本扫描状态
    TS_INIT,
    TS_STAR,      // 起始的星号序列：*强调、**着重、***
    TS_STAR_TEXT, // 星号中间的文本
    TS_UNSTAR,    // 结束的星号序列
    TS_CODE,      // 代码节（遇到反引号了）
  }ts = TS_INIT, ts_bak = TS_INIT; // 状态变量、状态备份
  int star_level = 0; // 星号层级
  string star_end; // 星号块的结束标签
  for(const int z = s.size(); i < z; ++i){
    const char c = s[i];
    switch(ts){
    case TS_INIT:
      if(c == '*'){ // 遇到星号
        ts = TS_STAR;
        star_level = 1;
      }else if(c == '`'){ // 遇到反引号，开始代码
        ts_bak = ts;
        ts = TS_CODE;
        ost << "<code>";
      }else{
        ost << c; // 普通字符，原样输出
      }
      break;
    case TS_STAR:
      if(c == '*'){ // 继续星号
        ++star_level;
      }else{ // 遇到非星号，根据之前的星号个数确定修饰
        if(star_level == 1){
          ost << "<em>"; star_end = "</em>";
        }else if(star_level == 2){
          ost << "<strong>"; star_end = "</strong>";
        }else if(star_level == 3){
          ost << "<strong><em>"; star_end = "</em></strong>";
        }else{
          assert(false);
        }
        if(c == '`'){ // 星号中包含代码
          ts_bak = TS_STAR_TEXT; // 记住之前的状态
          ts = TS_CODE; // 切换成代码状态
          ost << "<code>";
        }else{ // 星号中普通文本
          ost << c;
          ts = TS_STAR_TEXT;
        }
      }
      break;
    case TS_STAR_TEXT:
      if(c == '*'){
        --star_level;
        if(star_level == 0){ // 上一区结束，新的着重区开始
          // TODO: 星号修饰结束，输出结束标记、恢复ts状态
          assert(star_end == "</em>");
        }else{
          ts = TS_UNSTAR; // 进入结束星号
        }
      }else if(c == '`'){
        // TODO: 星号中包含代码
      }else{
        ost << c; // 输出普通字符
      }
      break;
    case TS_UNSTAR:
      if(c == '*'){ // TODO: **内嵌*，***内嵌*或**
        --star_level;
        if(star_level == 0){
          // TODO: 修饰结束
        }
        assert(star_level >= 0); // 不应降成负的
      }else{
        assert(false); // 异常情况：星号不匹配
      }
      break;
    case TS_CODE:
      if(c == '`'){
        // TODO: 代码结束
      }else{
        // TODO: 代码中的字符
      }
      break;
    default:
      assert(false);
      break;
    }
  }
}

void Markdown::scan_line(const string& s){
  int heading_level = 0;
  int hyphen_level = 0;
  for(int i = 0, z = s.size(); i < z; ++i){
    const char c = s[i];
    switch(state){
    case S_INIT:
      assert(!s.empty()); // 不允许随意空行
      if(c == '#'){
        // TODO: 设置标题状态、层级
      }else if(c == '-'){
        // TODO: 设置连字符状态，层级
      }else if(block == B_INIT){ // 之前没在段落中
        block = B_PARAGRAPH; // 进入段落
        ost << "<p>"; // 新开段落
        scan_text(s, i); // 处理文本行
        return;
      }else if(block == B_PARAGRAPH){ // 之前已在段落中
        ost << "<br>\n"; // 结束前一行
        scan_text(s, i);
        return;
      }else{
        assert(false);
      }
      break;
    case S_HEADING:
      if(c == '#'){
        ++heading_level;
      }else if(c == ' '){
        assert(heading_level >= 1 && heading_level <= 6);
        // TODO: 输出标题、恢复状态
        return;
      }else{
        assert(false);
      }
      break;
    case S_HYPHEN:
      if(c == '-'){
        ++hyphen_level;
      }else if(c == ' '){
        if(hyphen_level == 1){
          if(block == B_INIT){
            // TODO: 设置block状态、输出列表开始标记
          }
          ulist_item(s.substr(i + 1));
          return;
        }else{
          assert(false);
        }
      }else{
        assert(false);
      }
      break;
    default:
      assert(false);
      break;
    }
  }
  if(state == S_HYPHEN){
    if(hyphen_level == 3){
      // TODO: 分隔线
    }else{
      assert(false);
    }
  }else{
    assert(state == S_INIT);
  }
}

void Markdown::end_block(bool accept_init){
  switch(block){
  case B_INIT:
    assert(accept_init);
    break;
  case B_ULIST:
    ulist_end();
    break;
  case B_PARAGRAPH:
    paragraph_end();
    break;
  default:
    assert(false);
    break;
  }
}

void Markdown::render(){
  begin_html();
  for(string s; getline(ist, s);){
    if(s.empty()){ // 空行表示块的结束
      end_block(false); // 之前状态必须是列表或段落，不接受初始状态（多余空行）
    }else{
      scan_line(s);
    }
  }
  end_block(true);
  end_html();
}

void Markdown::begin_html(){
  const char head[] = R"delimiter(<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Markdown</title>
</head>
<body>
)delimiter";
  ost << head;
}

void Markdown::end_html(){
  const char tail[] = R"delimiter(</body>
</html>
)delimiter";
  ost << tail << flush;
}

int main(){
  Markdown md(cin, cout);
  md.render();
}